//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

//*****************************************************************************
// This code supports formatting a method and it's signature in a friendly
// and consistent format.
//
//*****************************************************************************
#include "stdafx.h"
#include "prettyprintsig.h"
#include "utilcode.h"
#include "metadata.h"
#include "corpriv.h"

/***********************************************************************/
// Null-terminates the string held in "out"

static WCHAR* asStringW(CQuickBytes *out) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return NULL;);
    }
    CONTRACTL_END

    SIZE_T oldSize = out->Size();
    if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
        return 0;
    WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
    *cur = 0;
    return((WCHAR*) out->Ptr()); 
} // static WCHAR* asStringW()

// Null-terminates the string held in "out"

static CHAR* asStringA(CQuickBytes *out) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return NULL;);
    }
    CONTRACTL_END

    SIZE_T oldSize = out->Size();
    if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
        return 0;
    CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
    *cur = 0;
    return((CHAR*) out->Ptr()); 
} // static CHAR* asStringA()

/***********************************************************************/
// Appends the str to "out"
// The string held in "out" is not NULL-terminated. asStringW() needs to
// be called for the NULL-termination

static HRESULT appendStrW(CQuickBytes *out, const WCHAR* str) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    SIZE_T len = wcslen(str) * sizeof(WCHAR); 
    SIZE_T oldSize = out->Size();
    if (FAILED(out->ReSizeNoThrow(oldSize + len)))
        return E_OUTOFMEMORY;
    WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
    memcpy(cur, str, len);
    // Note no trailing null!
    return S_OK;
} // static HRESULT appendStrW()

// Appends the str to "out"
// The string held in "out" is not NULL-terminated. asStringA() needs to
// be called for the NULL-termination

static HRESULT appendStrA(CQuickBytes *out, const CHAR* str) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    SIZE_T len = strlen(str) * sizeof(CHAR); 
    SIZE_T oldSize = out->Size();
    if (FAILED(out->ReSizeNoThrow(oldSize + len)))
        return E_OUTOFMEMORY;
    CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
    memcpy(cur, str, len);
    // Note no trailing null!
    return S_OK;
} // static HRESULT appendStrA()


static HRESULT appendStrNumW(CQuickBytes *out, int num) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    WCHAR buff[32];
    swprintf_s(buff, 32, W("%d"), num);
    return appendStrW(out, buff);
} // static HRESULT appendStrNumW()

static HRESULT appendStrNumA(CQuickBytes *out, int num) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    CHAR buff[32];
    sprintf_s(buff, 32, "%d", num);
    return appendStrA(out, buff);
} // static HRESULT appendStrNumA()

static HRESULT appendStrHexW(CQuickBytes *out, int num) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    WCHAR buff[32];
    swprintf_s(buff, 32, W("%08X"), num);
    return appendStrW(out, buff);
} // static HRESULT appendStrHexW()

static HRESULT appendStrHexA(CQuickBytes *out, int num) 
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    CHAR buff[32];
    sprintf_s(buff, 32, "%08X", num);
    return appendStrA(out, buff);
} // static HRESULT appendStrHexA()

/***********************************************************************/

LPCWSTR PrettyPrintSigWorker(
               PCCOR_SIGNATURE &  typePtr,      // type to convert,     
               size_t             typeLen,      // length of type
               const WCHAR      * name,         // can be "", the name of the method for this sig       
               CQuickBytes *      out,          // where to put the pretty printed string       
               IMetaDataImport *  pIMDI);       // Import api to use.

//*****************************************************************************
//*****************************************************************************
// pretty prints 'type' to the buffer 'out' returns a poitner to the next type, 
// or 0 on a format failure 

static PCCOR_SIGNATURE PrettyPrintType(
               PCCOR_SIGNATURE    typePtr,      // type to convert,     
               size_t             typeLen,      // Maximum length of the type
               CQuickBytes *      out,          // where to put the pretty printed string       
               IMetaDataImport *  pIMDI)        // ptr to IMDInternal class with ComSig
{
    mdToken             tk;     
    const WCHAR *       str;    
    WCHAR               rcname[MAX_CLASS_NAME];
    HRESULT             hr;
    unsigned __int8     elt = *typePtr++;
    PCCOR_SIGNATURE     typeEnd = typePtr + typeLen;

    switch(elt) 
    {
    case ELEMENT_TYPE_VOID:
        str = W("void");
        goto APPEND;
        
    case ELEMENT_TYPE_BOOLEAN:
        str = W("bool");
        goto APPEND;
        
    case ELEMENT_TYPE_CHAR:
        str = W("wchar");
        goto APPEND; 
        
    case ELEMENT_TYPE_I1:
        str = W("int8");
        goto APPEND;
        
    case ELEMENT_TYPE_U1:
        str = W("unsigned int8");
        goto APPEND;
        
    case ELEMENT_TYPE_I2:
        str = W("int16");
        goto APPEND;
        
    case ELEMENT_TYPE_U2:
        str = W("unsigned int16");
        goto APPEND;
        
    case ELEMENT_TYPE_I4:
        str = W("int32");
        goto APPEND;
        
    case ELEMENT_TYPE_U4:
        str = W("unsigned int32");
        goto APPEND;
        
    case ELEMENT_TYPE_I8:
        str = W("int64");
        goto APPEND;
        
    case ELEMENT_TYPE_U8:
        str = W("unsigned int64");
        goto APPEND;
        
    case ELEMENT_TYPE_R4:
        str = W("float32");
        goto APPEND;
        
    case ELEMENT_TYPE_R8:
        str = W("float64");
        goto APPEND;
        
    case ELEMENT_TYPE_U:
        str = W("unsigned int");
        goto APPEND;
        
    case ELEMENT_TYPE_I:
        str = W("int");
        goto APPEND;
        
    case ELEMENT_TYPE_OBJECT:
        str = W("class System.Object");
        goto APPEND;
        
    case ELEMENT_TYPE_STRING:
        str = W("class System.String");
        goto APPEND;
        
    case ELEMENT_TYPE_CANON_ZAPSIG:
        str = W("class System.__Canon");
        goto APPEND;
        
    case ELEMENT_TYPE_TYPEDBYREF:
        str = W("refany");
        goto APPEND;
        
    APPEND: 
        appendStrW(out, str);
        break;

    case ELEMENT_TYPE_VALUETYPE:
        str = W("value class ");
        goto DO_CLASS;
        
    case ELEMENT_TYPE_CLASS:
        str = W("class "); 
        goto DO_CLASS;
        
    DO_CLASS:
        typePtr += CorSigUncompressToken(typePtr, &tk);
        appendStrW(out, str);
        rcname[0] = 0;
        str = rcname;
        
        if (TypeFromToken(tk) == mdtTypeRef)
        {
            hr = pIMDI->GetTypeRefProps(tk, 0, rcname, NumItems(rcname), 0);
        }
        else if (TypeFromToken(tk) == mdtTypeDef)
        {
            hr = pIMDI->GetTypeDefProps(tk, rcname, NumItems(rcname), 0, 0, 0);
        }
        else
        {
            _ASSERTE(!"Unknown token type encountered in signature.");
            str = W("<UNKNOWN>");
        }
        
        appendStrW(out, str);
        break;
        
    case ELEMENT_TYPE_SZARRAY:
        typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
        appendStrW(out, W("[]"));
        break;
    
    case ELEMENT_TYPE_ARRAY:
        {
            typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
            unsigned rank = CorSigUncompressData(typePtr);
            PREFIX_ASSUME(rank <= 0xffffff);
            
            // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
            if (rank == 0)
            {
                appendStrW(out, W("[??]"));
            }
            else 
            {
                _ASSERTE(rank != 0);
                int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
                int* sizes               = &lowerBounds[rank];
                memset(lowerBounds, 0, sizeof(int)*2*rank); 
                
                unsigned numSizes = CorSigUncompressData(typePtr);
                _ASSERTE(numSizes <= rank);
                unsigned int i;
                for(i =0; i < numSizes; i++)
                    sizes[i] = CorSigUncompressData(typePtr);
                
                unsigned numLowBounds = CorSigUncompressData(typePtr);
                _ASSERTE(numLowBounds <= rank); 
                for(i = 0; i < numLowBounds; i++)       
                    lowerBounds[i] = CorSigUncompressData(typePtr); 
                
                appendStrW(out, W("["));
                for(i = 0; i < rank; i++)       
                {       
                    if (sizes[i] != 0 && lowerBounds[i] != 0)   
                    {   
                        if (lowerBounds[i] == 0)        
                            appendStrNumW(out, sizes[i]);
                        else    
                        {       
                            appendStrNumW(out, lowerBounds[i]);
                            appendStrW(out, W("..."));
                            if (sizes[i] != 0)  
                                appendStrNumW(out, lowerBounds[i] + sizes[i] + 1);
                        }       
                    }   
                    if (i < rank-1) 
                        appendStrW(out, W(","));
                }       
                appendStrW(out, W("]"));  
            }
        }
        break;
        
    case ELEMENT_TYPE_MVAR:
        appendStrW(out, W("!!"));
        appendStrNumW(out, CorSigUncompressData(typePtr));
        break;
        
    case ELEMENT_TYPE_VAR:   
        appendStrW(out, W("!"));  
        appendStrNumW(out, CorSigUncompressData(typePtr));
        break;
        
    case ELEMENT_TYPE_GENERICINST:
        {
            typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
            unsigned ntypars = CorSigUncompressData(typePtr);
            appendStrW(out, W("<"));
            for (unsigned i = 0; i < ntypars; i++)
            {
                if (i > 0)
                    appendStrW(out, W(","));
                typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
            }
            appendStrW(out, W(">"));
        }
        break;
        
    case ELEMENT_TYPE_MODULE_ZAPSIG:
        appendStrW(out, W("[module#"));
        appendStrNumW(out, CorSigUncompressData(typePtr));
        appendStrW(out, W(", token#"));
        typePtr += CorSigUncompressToken(typePtr, &tk); 
        appendStrHexW(out, tk);
        appendStrW(out, W("]"));
        break;
            
    case ELEMENT_TYPE_FNPTR:
        appendStrW(out, W("fnptr "));
        PrettyPrintSigWorker(typePtr, (typeEnd - typePtr), W(""), out, pIMDI);
        break;

    case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
    case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
        appendStrW(out, W("native "));
        typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
        break;

    // Modifiers or depedant types  
    case ELEMENT_TYPE_PINNED:
        str = W(" pinned");
        goto MODIFIER;
        
    case ELEMENT_TYPE_PTR:
        str = W("*");
        goto MODIFIER;
        
    case ELEMENT_TYPE_BYREF:   
        str = W("&");
        goto MODIFIER;

    MODIFIER:
        typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
        appendStrW(out, str);
        break;
        
    default:
    case ELEMENT_TYPE_SENTINEL:
    case ELEMENT_TYPE_END:
        _ASSERTE(!"Unknown Type");
        return(typePtr);
        break;
    }
    return(typePtr);
} // static PCCOR_SIGNATURE PrettyPrintType()

//*****************************************************************************
// Converts a com signature to a text signature.
//
// Note that this function DOES NULL terminate the result signature string.
//*****************************************************************************
LPCWSTR PrettyPrintSigLegacy(
        PCCOR_SIGNATURE   typePtr,      // type to convert,     
        unsigned          typeLen,      // length of type
        const WCHAR     * name,         // can be "", the name of the method for this sig       
        CQuickBytes     * out,          // where to put the pretty printed string       
        IMetaDataImport * pIMDI)        // Import api to use.
{
    return PrettyPrintSigWorker(typePtr, typeLen, name, out, pIMDI);
} // LPCWSTR PrettyPrintSigLegacy()

LPCWSTR PrettyPrintSigWorker(
        PCCOR_SIGNATURE & typePtr,      // type to convert,     
        size_t            typeLen,      // length of type
        const WCHAR     * name,         // can be "", the name of the method for this sig       
        CQuickBytes     * out,          // where to put the pretty printed string       
        IMetaDataImport * pIMDI)        // Import api to use.
{
    out->Shrink(0); 
    unsigned numTyArgs = 0;
    unsigned numArgs;
    PCCOR_SIGNATURE typeEnd = typePtr + typeLen;    // End of the signature.

    if (name != 0)                      // 0 means a local var sig
    {
        // get the calling convention out
        unsigned callConv = CorSigUncompressData(typePtr);
        
        if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
        {
            PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
            if (name != 0 && *name != 0)
            {
                appendStrW(out, W(" "));
                appendStrW(out, name);
            }
            return(asStringW(out));
        }
        
        if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
            appendStrW(out, W("instance "));
        
        if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
        {
            appendStrW(out, W("generic "));
            numTyArgs = CorSigUncompressData(typePtr);
        }
        
        static const WCHAR * const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] = 
        {
            W(""), 
            W("unmanaged cdecl "), 
            W("unmanaged stdcall "),
            W("unmanaged thiscall "),
            W("unmanaged fastcall "),
            W("vararg "),
            W("<error> "),
            W("<error> "),
            W(""),
            W(""),
            W(""),
            W("native vararg ")
        };

        if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
        {
        appendStrW(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
        }

        
        numArgs = CorSigUncompressData(typePtr);
        // do return type
        typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
        
    }
    else
    {
        numArgs = CorSigUncompressData(typePtr);
    }
    
    if (name != 0 && *name != 0)
    {
        appendStrW(out, W(" "));
        appendStrW(out, name);
    }
    appendStrW(out, W("("));
    
    bool needComma = false;
    
    while (numArgs)
    {
        if (typePtr >= typeEnd) 
            break;

        if (*typePtr == ELEMENT_TYPE_SENTINEL) 
        {
            if (needComma)
                appendStrW(out, W(","));
            appendStrW(out, W("..."));  
            typePtr++;
        }
        else 
        {
            if (numArgs <= 0)
                break;
            if (needComma)
                appendStrW(out, W(","));
            typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI); 
            --numArgs;
        }
        needComma = true;
    }
    appendStrW(out, W(")"));
    return (asStringW(out));
} // LPCWSTR PrettyPrintSigWorker()


// Internal implementation of PrettyPrintSig().

HRESULT PrettyPrintSigWorkerInternal(
        PCCOR_SIGNATURE   & typePtr,    // type to convert,     
        size_t              typeLen,    // length of type
        const CHAR        * name,       // can be "", the name of the method for this sig       
        CQuickBytes       * out,        // where to put the pretty printed string       
        IMDInternalImport * pIMDI);     // Import api to use.

#ifdef _PREFAST_
#pragma warning(push)
#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
#endif
//*****************************************************************************
//*****************************************************************************
// pretty prints 'type' to the buffer 'out' returns a poitner to the next type, 
// or 0 on a format failure 

__checkReturn 
static HRESULT PrettyPrintTypeA(
    PCCOR_SIGNATURE   &typePtr,     // type to convert,
    size_t             typeLen,     // Maximum length of the type.
    CQuickBytes       *out,         // where to put the pretty printed string
    IMDInternalImport *pIMDI)       // ptr to IMDInternal class with ComSig
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    mdToken     tk;                     // A type's token.
    const CHAR  *str;                   // Temporary string.
    LPCUTF8     pNS;                    // A type's namespace.
    LPCUTF8     pN;                     // A type's name.
    HRESULT     hr;                     // A result.

    PCCOR_SIGNATURE typeEnd = typePtr + typeLen;    // End of the signature.
    unsigned __int8     elt = *typePtr++;

    switch(elt)  {
    case ELEMENT_TYPE_VOID:
        str = "void";
        goto APPEND;
        
    case ELEMENT_TYPE_BOOLEAN:
        str = "bool";
        goto APPEND;
        
    case ELEMENT_TYPE_CHAR:
        str = "wchar";
        goto APPEND;
        
    case ELEMENT_TYPE_I1:
        str = "int8";
        goto APPEND;
        
    case ELEMENT_TYPE_U1:
        str = "unsigned int8";
        goto APPEND;
        
    case ELEMENT_TYPE_I2:
        str = "int16";
        goto APPEND;
        
    case ELEMENT_TYPE_U2:
        str = "unsigned int16";
        goto APPEND;
        
    case ELEMENT_TYPE_I4:
        str = "int32";
        goto APPEND;
        
    case ELEMENT_TYPE_U4:
        str = "unsigned int32";
        goto APPEND;
        
    case ELEMENT_TYPE_I8:
        str = "int64";
        goto APPEND;
        
    case ELEMENT_TYPE_U8:
        str = "unsigned int64";
        goto APPEND;
        
    case ELEMENT_TYPE_R4:
        str = "float32";
        goto APPEND;
        
    case ELEMENT_TYPE_R8:
        str = "float64";
        goto APPEND;
        
    case ELEMENT_TYPE_U:
        str = "unsigned int";
        goto APPEND;
        
    case ELEMENT_TYPE_I:
        str = "int";
        goto APPEND;
        
    case ELEMENT_TYPE_OBJECT:
        str = "class System.Object";
        goto APPEND;
        
    case ELEMENT_TYPE_STRING:
        str = "class System.String";
        goto APPEND;
        
    case ELEMENT_TYPE_CANON_ZAPSIG:
        str = "class System.__Canon";
        goto APPEND;
        
    case ELEMENT_TYPE_TYPEDBYREF:
        str = "refany";
        goto APPEND;
        
    APPEND: 
        IfFailGo(appendStrA(out, str));
        break;
        
    case ELEMENT_TYPE_INTERNAL:
        void* pMT;
        pMT = *((void* UNALIGNED *)typePtr);
        typePtr += sizeof(void*);
        CHAR tempBuffer[64];
        sprintf_s(tempBuffer, 64, "pMT: %p", pMT);
        IfFailGo(appendStrA(out, tempBuffer));
        break;
        
    case ELEMENT_TYPE_VALUETYPE:
        str = "value class ";
        goto DO_CLASS;
        
    case ELEMENT_TYPE_CLASS:
        str = "class "; 
        goto DO_CLASS;
        
    case ELEMENT_TYPE_CMOD_REQD:
        str = "required_modifier ";
        goto DO_CLASS;
        
    case ELEMENT_TYPE_CMOD_OPT:
        str = "optional_modifier ";
        goto DO_CLASS;
        
    DO_CLASS:
        typePtr += CorSigUncompressToken(typePtr, &tk); 
        IfFailGo(appendStrA(out, str));
        str = "<UNKNOWN>";
        
        if (TypeFromToken(tk) == mdtTypeSpec)
        {
            ULONG cSig;
            PCCOR_SIGNATURE sig;
            IfFailGo(pIMDI->GetSigFromToken(tk, &cSig, &sig));
            IfFailGo(PrettyPrintTypeA(sig, cSig, out, pIMDI));
        }
        else
        {
            if (TypeFromToken(tk) == mdtTypeRef)
            {
                //<TODO>@consider: assembly name?</TODO>
                if (FAILED(pIMDI->GetNameOfTypeRef(tk, &pNS, &pN)))
                {
                    pNS = pN = "Invalid TypeRef record";
                }
            }
            else
            {
                _ASSERTE(TypeFromToken(tk) == mdtTypeDef);
                if (FAILED(pIMDI->GetNameOfTypeDef(tk, &pN, &pNS)))
                {
                    pNS = pN = "Invalid TypeDef record";
                }
            }
            
            if (pNS && *pNS)
            {
                IfFailGo(appendStrA(out, pNS));
                IfFailGo(appendStrA(out, NAMESPACE_SEPARATOR_STR));
            }
            IfFailGo(appendStrA(out, pN));
        }
        break;
        
    case ELEMENT_TYPE_SZARRAY:
        IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
        IfFailGo(appendStrA(out, "[]"));
        break;
    
    case ELEMENT_TYPE_ARRAY:
        {
            IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
            unsigned rank = CorSigUncompressData(typePtr);
            PREFIX_ASSUME(rank <= 0xffffff);
            // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
            if (rank == 0) 
            {
                IfFailGo(appendStrA(out, "[??]"));
            }
            else 
            {
                _ASSERTE(rank != 0);
                int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
                int* sizes       = &lowerBounds[rank];
                memset(lowerBounds, 0, sizeof(int)*2*rank); 
                
                unsigned numSizes = CorSigUncompressData(typePtr);
                _ASSERTE(numSizes <= rank);
                unsigned int i;
                for(i =0; i < numSizes; i++)
                    sizes[i] = CorSigUncompressData(typePtr);
                
                unsigned numLowBounds = CorSigUncompressData(typePtr);
                _ASSERTE(numLowBounds <= rank); 
                for(i = 0; i < numLowBounds; i++)
                    lowerBounds[i] = CorSigUncompressData(typePtr); 
                
                IfFailGo(appendStrA(out, "["));
                for(i = 0; i < rank; i++)
                {
                    if (sizes[i] != 0 && lowerBounds[i] != 0)
                    {
                        if (lowerBounds[i] == 0)
                            appendStrNumA(out, sizes[i]);
                        else
                         {
                             appendStrNumA(out, lowerBounds[i]);
                             IfFailGo(appendStrA(out, "..."));
                             if (sizes[i] != 0)
                                 appendStrNumA(out, lowerBounds[i] + sizes[i] + 1);
                         }
                    }
                    if (i < rank-1) 
                        IfFailGo(appendStrA(out, ","));
                }
                IfFailGo(appendStrA(out, "]"));  
            }
        } 
        break;

    case ELEMENT_TYPE_MVAR:
        IfFailGo(appendStrA(out, "!!"));  
        appendStrNumA(out, CorSigUncompressData(typePtr));
        break;
        
    case ELEMENT_TYPE_VAR:   
        IfFailGo(appendStrA(out, "!"));  
        appendStrNumA(out, CorSigUncompressData(typePtr));
        break;
        
    case ELEMENT_TYPE_GENERICINST:
        {
            IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
            unsigned ntypars = CorSigUncompressData(typePtr);
            IfFailGo(appendStrA(out, "<"));
            for (unsigned i = 0; i < ntypars; i++)
            {
                if (i > 0)
                    IfFailGo(appendStrA(out, ","));
                IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
            }
            IfFailGo(appendStrA(out, ">"));
        }
        break;
        
    case ELEMENT_TYPE_MODULE_ZAPSIG:
        IfFailGo(appendStrA(out, "[module#"));
        appendStrNumA(out, CorSigUncompressData(typePtr));
        IfFailGo(appendStrA(out, ", token#"));
        typePtr += CorSigUncompressToken(typePtr, &tk); 
        IfFailGo(appendStrHexA(out, tk));
        IfFailGo(appendStrA(out, "]"));
        break;
        
    case ELEMENT_TYPE_FNPTR:
        IfFailGo(appendStrA(out, "fnptr "));
        IfFailGo(PrettyPrintSigWorkerInternal(typePtr, (typeEnd - typePtr), "", out,pIMDI));
        break;
        
    case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
    case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
        IfFailGo(appendStrA(out, "native "));
        IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
        break;

        // Modifiers or dependent types  
    case ELEMENT_TYPE_PINNED:
        str = " pinned";
        goto MODIFIER; 
        
    case ELEMENT_TYPE_PTR:   
        str = "*";
        goto MODIFIER;
        
    case ELEMENT_TYPE_BYREF:   
        str = "&";
        goto MODIFIER;

    MODIFIER:
        IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
        IfFailGo(appendStrA(out, str));
        break;
        
    default:
    case ELEMENT_TYPE_SENTINEL:
    case ELEMENT_TYPE_END:
        hr = E_INVALIDARG;
        break;
    }
 ErrExit:
    return hr;
} // PrettyPrintTypeA
#ifdef _PREFAST_
#pragma warning(pop)
#endif

//*****************************************************************************
// Converts a com signature to a text signature.
//
// Note that this function DOES NULL terminate the result signature string.
//*****************************************************************************
HRESULT PrettyPrintSigInternalLegacy(
          PCCOR_SIGNATURE     typePtr,  // type to convert,     
          unsigned            typeLen,  // length of type
          const CHAR        * name,     // can be "", the name of the method for this sig       
          CQuickBytes       * out,      // where to put the pretty printed string       
          IMDInternalImport * pIMDI)    // Import api to use.
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    return PrettyPrintSigWorkerInternal(typePtr, typeLen, name, out, pIMDI);
} // HRESULT PrettyPrintSigInternalLegacy()

HRESULT PrettyPrintSigWorkerInternal(
          PCCOR_SIGNATURE   & typePtr,  // type to convert,     
          size_t              typeLen,  // length of type
          const CHAR        * name,     // can be "", the name of the method for this sig       
          CQuickBytes       * out,      // where to put the pretty printed string       
          IMDInternalImport * pIMDI)    // Import api to use.
{
    CONTRACTL
    {
        NOTHROW;
        INJECT_FAULT(return E_OUTOFMEMORY;);
    }
    CONTRACTL_END

    HRESULT     hr = S_OK;
    unsigned    numArgs;     // Count of arugments to function, or count of local vars.
    unsigned numTyArgs = 0;
    PCCOR_SIGNATURE typeEnd = typePtr + typeLen;
    bool needComma = false;
    
    out->Shrink(0); 
    
    if (name != 0)           // 0 means a local var sig 
    {
        // get the calling convention out
        unsigned callConv = CorSigUncompressData(typePtr);
        
        if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
        {
            IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
            if (name != 0 && *name != 0)

            {
                IfFailGo(appendStrA(out, " "));
                IfFailGo(appendStrA(out, name));
            }
            goto ErrExit;
        }
        
        if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
            IfFailGo(appendStrA(out, "instance "));
        
        if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
        {
            IfFailGo(appendStrA(out, "generic "));
            numTyArgs = CorSigUncompressData(typePtr);
        }
        
        static const CHAR* const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] = 
        {
            "", 
            "unmanaged cdecl ", 
            "unmanaged stdcall ",
            "unmanaged thiscall ",
            "unmanaged fastcall ",
            "vararg ",
            "<error> ",
            "<error> ",
            "",
            "",
            "",
            "native vararg "
        };

        if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
        {
        appendStrA(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
        }
        
        numArgs = CorSigUncompressData(typePtr);
        // do return type
        IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
    }
    else
    {
        numArgs = CorSigUncompressData(typePtr);
    }
    
    if (name != 0 && *name != 0)
    {
        IfFailGo(appendStrA(out, " "));
        IfFailGo(appendStrA(out, name));
    }
    IfFailGo(appendStrA(out, "("));
    
    while (numArgs)
    {
        if (typePtr >= typeEnd) 
            break;

        if (*typePtr == ELEMENT_TYPE_SENTINEL) 
        {
            if (needComma)
                IfFailGo(appendStrA(out, ","));
            IfFailGo(appendStrA(out, "..."));  
            ++typePtr;
        }
        else 
        {
            if (needComma)
                IfFailGo(appendStrA(out, ","));
            IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI)); 
            --numArgs;
        }
        needComma = true;
    }
    IfFailGo(appendStrA(out, ")"));
    if (asStringA(out) == 0)
        IfFailGo(E_OUTOFMEMORY);
    
 ErrExit:
    return hr;
} // HRESULT PrettyPrintSigWorkerInternal()
